package com.nowcoder.topic.unionfind.hard;

import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.PriorityQueue;

/**
 * NC95 数组中的最长连续子序列
 * @author d3y1
 */
public class NC95 {
    /**
     * 代码中的类名、方法名、参数名已经指定，请勿修改，直接返回方法规定的值即可
     *
     * max increasing subsequence
     * @param arr int整型一维数组 the array
     * @return int整型
     */
    public int MLS (int[] arr) {
        // return solution1(arr);
        // return solution2(arr);
        // return solution3(arr);
        // return solution4(arr);
        return solution5(arr);
    }

    /**
     * 小根堆
     * @param arr
     * @return
     */
    private int solution1(int[] arr){
        PriorityQueue<Integer> minHeap = new PriorityQueue<>();
        for(int num: arr){
            minHeap.offer(num);
        }

        int result = 0;
        int len = 1;
        int pre = minHeap.poll();
        int cur;
        while(!minHeap.isEmpty()){
            cur = minHeap.poll();
            if(cur == pre){
                continue;
            }
            else if(cur == pre+1){
                len++;
                result = Math.max(result, len);
                pre = cur;
            }
            else{
                len = 1;
                pre = cur;
            }
        }

        return result;
    }

    /**
     * 排序
     * @param arr
     * @return
     */
    private int solution2(int[] arr){
        int n = arr.length;
        Arrays.sort(arr);

        int result = 0;
        int len = 1;
        for(int i=0; i+1<n; i++){
            if(arr[i+1] == arr[i]){
                continue;
            }
            else if(arr[i+1] == arr[i]+1){
                len++;
                result = Math.max(result, len);
            }
            else{
                len = 1;
            }
        }

        return result;
    }

    /**
     * HashSet
     * @param arr
     * @return
     */
    private int solution3(int[] arr){
        HashSet<Integer> set = new HashSet<>();

        for(int num: arr){
            set.add(num);
        }

        int result = 0;
        int len;
        for(int num: arr){
            if(set.contains(num-1)){
                continue;
            }

            int start = num;
            len = 1;
            while(set.contains(++start)){
                len++;
            }

            result = Math.max(result, len);
        }

        return result;
    }

    /**
     * HashMap
     * @param arr
     * @return
     */
    private int solution4(int[] arr){
        HashMap<Integer, Integer> map = new HashMap<>();

        int result = 0;
        int leftLen,rightLen,curLen;
        for(int num: arr){
            if(!map.containsKey(num)){
                leftLen = map.getOrDefault(num-1, 0);
                rightLen = map.getOrDefault(num+1, 0);
                curLen = leftLen+1+rightLen;
                map.put(num, curLen);
                result = Math.max(result, curLen);
                map.put(num-leftLen, curLen);
                map.put(num+rightLen, curLen);
            }
        }

        return result;
    }

    /**
     * 并查集
     * @param arr
     * @return
     */
    private int solution5(int[] arr){
        UnionFind uf = new UnionFind(arr);

        HashSet<Integer> set = new HashSet<>();
        for(int num: arr){
            set.add(num);
        }

        for(int num: arr){
            if(set.contains(num-1)){
                uf.union(num, num-1);
            }
        }

        return uf.maxSize();
    }

    private class UnionFind {
        int size;
        HashMap<Integer, Integer> parent = new HashMap<>();
        HashMap<Integer, Integer> sizeMap = new HashMap<>();

        UnionFind(int[] nums){
            this.size = 0;
            for(int num: nums){
                parent.put(num, num);
                sizeMap.put(num, 1);
            }
        }

        int find(int x){
            int par = parent.get(x);
            while(x != par){
                parent.put(x, parent.get(par));
                x = par;
                par = parent.get(x);
            }

            return x;
        }

        void union(int p, int q){
            int rootP = find(p);
            int rootQ = find(q);
            if(rootP != rootQ){
                int unionSize = sizeMap.get(rootP)+sizeMap.get(rootQ);
                if(rootP < rootQ){
                    parent.put(rootP, rootQ);
                    sizeMap.put(rootQ, unionSize);
                }else{
                    parent.put(rootQ, rootP);
                    sizeMap.put(rootP, unionSize);
                }
                this.size = Math.max(size, unionSize);
            }
        }

        int maxSize(){
            return this.size;
        }
    }
}